﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using PartialExpressionCompleter.Util;
using Microsoft.Cci;

namespace PartialExpressionCompleter.Query
{
    static class ExpressionEnumerator
    {
        public static IEnumerable<Expression> GetChildren<E>(this Expression exp, QueryContext<E> context) where E:IExpression
        {
            var type = exp.Type;
            if (type is IPointerType)
            {
                type = (type as IPointerType).TargetType.ResolvedType;
            }
            var baseCost = exp.BaseCost + (2 * ScoreSwitches.GetMultiplier(ScoreTypes.Depth));
            if (type != null)
            {
                foreach (var mem in context.VisibleZeroArgTypeMembers(type))
                {
                    if(mem is IFieldDefinition)
                    {
                        yield return new FieldLookup(exp, mem as IFieldDefinition)
                            .WithBaseCost(baseCost);
                    }
                    else if(mem is IPropertyDefinition)
                    {
                        yield return new PropertyLookup(exp, mem as IPropertyDefinition)
                            .WithBaseCost(baseCost);
                    }
                    else if(mem is IMethodDefinition)
                    {
                        yield return new MethodCall(mem as IMethodDefinition,
                                                    exp.SingletonList(), false)
                            .WithBaseCost(baseCost);
                    }
                }
            }
        }

        public static Expression GetParent(this Expression exp)
        {
            if (exp is PropertyLookup)
            {
                return (exp as PropertyLookup).Receiver;
            }
            else if (exp is FieldLookup)
            {
                return (exp as FieldLookup).Receiver;
            }
            else if (exp is MethodCall)
            {
                var call = exp as MethodCall;
                if (!call.IsStatic && call.Arguments.Count == 1)
                {
                    return call.Arguments.First();
                }
            }
            return null;
        }
        private static IEnumerable<Expression> _GetAncestors(this Expression exp)
        {
            var e = exp;
            while (e != null)
            {
                yield return e;
                e = e.GetParent();
            }
        }
        public static List<Expression> GetAncestors(this Expression exp)
        {
            return exp._GetAncestors().ToList();
        }

        public static IEnumerable<Expression> GetGlobals<E>(this QueryContext<E> context) where E : IExpression
        {
            return GetAllGlobals().Where(e => context.IsMemberVisible(e.TypeMember));
        }
        private static List<Expression> globals = null;
        public static IEnumerable<Expression> GetAllGlobals()
        {
            if (globals == null)
                globals = _GetAllGlobals().ToList();
            return globals;
        }
        private static IEnumerable<Expression> _GetAllGlobals()
        {
            var baseCost = 2 * ScoreSwitches.GetMultiplier(ScoreTypes.Depth);
            foreach (var fd in CCIUtil.GetAllStaticFields())
            {
                    yield return new StaticFieldLookup(fd).WithBaseCost(baseCost);
            }
            foreach (var pd in CCIUtil.GetAllStaticProperties())
            {
                    yield return new StaticPropertyLookup(pd).WithBaseCost(baseCost);
            }
            foreach (var meth in CCIUtil.GetAllStaticMethods())
            {
                if (meth.ParameterCount == 0)
                {
                    yield return new MethodCall(meth, new List<Expression>(), false)
                        .WithBaseCost(baseCost);
                }
            }
        }
        private static List<CompileTimeConstant> commonConstants = null;
        public static IEnumerable<Expression> GetCommonConstants()
        {
            if (commonConstants == null)
            {
                commonConstants = new List<CompileTimeConstant>();
                for (int i = 0; i <= 5; i++)
                {
                    commonConstants.Add(new CompileTimeConstant(
                        CCIUtil.GetByteType(), i));
                }

                commonConstants.Add(new CompileTimeConstant(
                    CCIUtil.GetBooleanType(), true));
                commonConstants.Add(new CompileTimeConstant(
                    CCIUtil.GetBooleanType(), false));

                commonConstants.Add(new StringCompileTimeConstant(
                    CCIUtil.GetType("System.String"), ""));

                foreach (var c in commonConstants)
                {
                    c.BaseCost = Expression.CONSTANT_COST;
                }
            }
            return commonConstants;
        }
        public static IEnumerable<Expression> GetDescendents<E>(this Expression exp,
                QueryContext<E> context, int depth) where E : IExpression
        {
            if (depth <= 0)
            {
                return exp.Singleton();
            }
            else
            {
                return exp.Singleton().Concat(exp.GetChildren(context)
                    .SelectMany(c => c.GetDescendents(context, depth - 1)));
            }
        }
        public static IEnumerable<Expression> GetLocals<E>(this QueryContext<E> context) where E : IExpression
        {
            var locals = context.Locals.Select(l => new LocalVariable(l) as Expression)
                .Concat(context.ContainingMethod.Parameters
                            .Select(p => new ParamVariable(p)))
                .ToList();
            if (!context.ContainingMethod.IsStatic)
            {
                locals.Add(new This(context.ContainingType));
            }
            return locals;
        }
        public static IEnumerable<Expression> GetAllSimpleExpressions<E>(
                this QueryContext<E> context) where E : IExpression
        {
            var depth = 2;
            var localRes = context.GetLocals()
                .SelectMany(e => e.GetDescendents(context, depth));
            var globalRes = context.GetGlobals();
            return localRes.Concat(globalRes);
                //.Concat(GetCommonConstants());
        }
    }
}
